" 类似VsCode使用快捷键，将代码块整体移动
" Maintainer:	dglxlcl
" Contributor:  dglxlcl
" Last Change:  2021-03-28
" Version: 0.1.0
" Homepage: none
" Repository: none
" License: MIT

let s:save_cpo = &cpo
set cpo&vim

if exists("g:loaded_blk_move") || &cp
	finish
endif

let g:loaded_blk_move = 1

"向上移动代码键盘映射，默认是<M-K>
if !exists('g:BlkMoveUpShortcut')
	let g:BlkMoveUpShortcut = '<M-k>'
end

"向下移动代码键盘映射,默认是<M-j>
if !exists('g:BlkMoveDownShortcut')
	let g:BlkMoveDownShortcut = '<M-j>'
end


"当前行向上移动
function! s:moveup_line()
	let cur_pos = getpos('.')		"获取当前光标位置
	
	"如果已经是最上一行，则直接返回
	if cur_pos[1] == 1
		return
	endif

	let preLineNum = cur_pos[1] - 1	"获得上一行的行号
	let tmp = getline(preLineNum)		"将上一行的内容临时存储
	call setline(preLineNum,getline(cur_pos[1]))	"将上一行的内容替换成当前行
	call setline(cur_pos[1],tmp)	"将当前行的内容替换成上一行
	let cur_pos[1] -= 1				"将当前光标的行号减1,得到目标位置"
	call setpos('.',cur_pos)		"修改当前光标位置
endfunction


"当前行向下移动
function! s:movedown_line()
	let cur_pos = getpos('.')		"获取当前光标位置
	
	"如果已经是最底一行，则直接返回
	if cur_pos[1] == line('$')
		return
	endif

	let nextLineNum = cur_pos[1] + 1	"获得下一行的行号
	let tmp = getline(nextLineNum)		"将下一行的内容临时存储
	call setline(nextLineNum,getline(cur_pos[1]))	"将下一行的内容替换成当前行
	call setline(cur_pos[1],tmp)	"将当前行的内容替换成下一行
	let cur_pos[1] += 1				"将当前光标的行号+1,得到目标位置"
	call setpos('.',cur_pos)		"修改当前光标位置
endfunction


"可视模式下，选择区域整体向上移动
function! s:moveup_multilines() range
	let start_mark = getpos("'<")	"获取选择区域起点坐标
	let end_mark = getpos("'>")		"获取选择区域终点坐标

	"如果选择区域起点在第一行，则直接退出函数
	if start_mark[1] == 1
		return
	endif

	let tmp_lines = getline(start_mark[1],end_mark[1])	"临时存储选择范围内容
	call add(tmp_lines, getline(start_mark[1] - 1))	"将上一行内容添加至临时存储后面
	call setline(start_mark[1]-1,tmp_lines)	"将修改后的内容写入buffer对应位置

	let save_curpos = getpos('.')	"保存当前坐标位置
	let start_mark[1] -= 1			"修改选择区域坐标值
	let end_mark[1] -= 1			"修改选择区域坐标值
	let save_curpos[1] -= 1			"修改当前坐标值
	call setpos("'<",start_mark)	"将修改后的几个坐标进行设置
	call setpos("'>",end_mark)
	call setpos('.',save_curpos)
endfunction


function! s:movedown_multilines() range
	let start_mark = getpos("'<")	"获取选择区域起点坐标
	let end_mark = getpos("'>")		"获取选择区域终点坐标
	
	"如果选择区域终点在最后一行，则直接退出函数
	if end_mark[1] == line('$')
		return
	endif

	let tmp_lines = getline(start_mark[1],end_mark[1])	"临时存储选择范围内容
	call insert(tmp_lines,getline(end_mark[1]+1)) "将后一行插入到选择范围前
	call setline(start_mark[1],tmp_lines) "将修改后的内容写入buffer对应位置

	let save_curpos = getpos('.')	"保存当前坐标位置
	let start_mark[1] += 1       	"修改选择区域坐标值
	let end_mark[1] += 1         	"修改选择区域坐标值
	let save_curpos[1] += 1      	"修改当前坐标值
	call setpos("'<",start_mark) 	"将修改后的几个坐标进行设置
	call setpos("'>",end_mark)
	call setpos('.',save_curpos)
endfunction


if g:BlkMoveUpShortcut != ''
	execute 'noremap <silent> '.BlkMoveUpShortcut.' :call <SID>moveup_line()<CR>'
	execute 'inoremap <silent> '.BlkMoveUpShortcut.' <ESC>:call <SID>moveup_line()<CR>a'
	execute 'vnoremap <silent> '.BlkMoveUpShortcut.' :call <SID>moveup_multilines()<CR>gv'
endif

if g:BlkMoveDownShortcut != ''
	execute 'noremap <silent> '.BlkMoveDownShortcut.' :call <SID>movedown_line()<CR>'
	execute 'inoremap <silent> '.BlkMoveDownShortcut.' <ESC>:call <SID>movedown_line()<CR>a'
	execute 'vnoremap <silent> '.BlkMoveDownShortcut.' :call <SID>movedown_multilines()<CR>gv'
endif


let &cpo = s:save_cpo
unlet s:save_cpo
